package com.pcbly.web.business.jbpm;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipInputStream;

import org.jbpm.api.Execution;
import org.jbpm.api.ExecutionService;
import org.jbpm.api.HistoryService;
import org.jbpm.api.IdentityService;
import org.jbpm.api.ManagementService;
import org.jbpm.api.NewDeployment;
import org.jbpm.api.ProcessDefinition;
import org.jbpm.api.ProcessDefinitionQuery;
import org.jbpm.api.ProcessEngine;
import org.jbpm.api.ProcessInstance;
import org.jbpm.api.ProcessInstanceQuery;
import org.jbpm.api.RepositoryService;
import org.jbpm.api.TaskService;
import org.jbpm.api.activity.ActivityExecution;
import org.jbpm.api.cmd.Environment;
import org.jbpm.api.history.HistoryActivityInstance;
import org.jbpm.api.history.HistoryActivityInstanceQuery;
import org.jbpm.api.identity.Group;
import org.jbpm.api.identity.User;
import org.jbpm.api.job.Job;
import org.jbpm.api.model.ActivityCoordinates;
import org.jbpm.api.task.Task;
import org.jbpm.pvm.internal.env.EnvironmentFactory;
import org.jbpm.pvm.internal.identity.impl.IdentitySessionImpl;
import org.jbpm.pvm.internal.identity.impl.UserImpl;
import org.jbpm.pvm.internal.identity.spi.IdentitySession;
import org.jbpm.pvm.internal.model.Activity;
import org.jbpm.pvm.internal.model.ActivityCoordinatesImpl;
import org.jbpm.pvm.internal.model.ActivityImpl;
import org.jbpm.pvm.internal.model.ExecutionImpl;
import org.jbpm.pvm.internal.model.ProcessDefinitionImpl;
import org.jbpm.pvm.internal.model.Transition;
import org.jbpm.pvm.internal.task.OpenTask;

import com.pcbly.web.persistence.jbpm.JbpmDao;

public class JbpmServiceImpl implements JbpmService {

	private ProcessEngine processEngine;
	private JbpmDao jbpmDao;

	public void setJbpmDao(JbpmDao jbpmDao) {
		this.jbpmDao = jbpmDao;
	}

	public void setProcessEngine(ProcessEngine processEngine) {
		this.processEngine = processEngine;
	}

	public JbpmServiceImpl() {

	}

	@Override
	public InputStream getResourceFromProcessDefinition(
			String processDefinitionId) throws Exception {
		// TODO Auto-generated method stub
		RepositoryService repositoryService = processEngine
				.getRepositoryService();
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
				.processDefinitionId(processDefinitionId).uniqueResult();
		if (pd != null) {
			String processName = pd.getName() + ".jpdl.xml";
			return repositoryService.getResourceAsStream(pd.getDeploymentId(),
					processName);
		}
		return null;
	}

	@Override
	public InputStream getResourceFromProcessInstance(String processInstanceId)
			throws Exception {
		// TODO Auto-generated method stub
		RepositoryService repositoryService = processEngine
				.getRepositoryService();
		ExecutionService executionService = processEngine.getExecutionService();
		ProcessInstanceQuery query = executionService
				.createProcessInstanceQuery();
		query.processInstanceId(processInstanceId);
		Execution processInstance = (Execution) query.uniqueResult();
		ProcessDefinition pd = ((ExecutionImpl) processInstance)
				.getProcessDefinition();
		if (pd != null) {
			String processName = pd.getName() + ".jpdl.xml";
			System.out.println("processName:  " + processName);
			return repositoryService.getResourceAsStream(pd.getDeploymentId(),
					processName);
		}
		return null;
	}

	@Override
	public List<ProcessDefinition> getLatestProcessDefinitions() {
		// TODO Auto-generated method stub
		RepositoryService repositoryService = processEngine
				.getRepositoryService();
		List<ProcessDefinition> definitions = repositoryService
				.createProcessDefinitionQuery().orderAsc(
						ProcessDefinitionQuery.PROPERTY_NAME).list();
		Map<String, ProcessDefinition> map = new LinkedHashMap<String, ProcessDefinition>();

		for (ProcessDefinition pd : definitions) {
			String key = pd.getKey();
			ProcessDefinition processDefinition = map.get(key);

			if ((processDefinition == null)
					|| (processDefinition.getVersion() < pd.getVersion())) {
				map.put(key, pd);
			}
		}

		return new ArrayList<ProcessDefinition>(map.values());
	}

	@Override
	public String reportMostActiveProcess() throws Exception {
		// TODO Auto-generated method stub
		return jbpmDao.reportMostActiveProcess();
	}

	@Override
	public String reportOverallActivity() throws Exception {
		// TODO Auto-generated method stub
		return jbpmDao.reportOverallActivity();
	}

	@Override
	public void addGroup(String name, String type, String parentGroupId)
			throws Exception {
		// TODO Auto-generated method stub
		IdentityService identityService = processEngine.getIdentityService();
		identityService.createGroup(name, type, parentGroupId);
	}

	@Override
	public void addMember(String userId, String groupId, String role)
			throws Exception {
		// TODO Auto-generated method stub
		IdentityService identityService = processEngine.getIdentityService();
		identityService.createMembership(userId, groupId, role);
	}

	@Override
	public void addUser(String id, String password, String givenName,
			String familyName) throws Exception {
		// TODO Auto-generated method stub
		IdentityService identityService = processEngine.getIdentityService();
		identityService.createUser(id, password, givenName, familyName);
	}

	@Override
	public void cancelTask(String dbid) throws Exception {
		// TODO Auto-generated method stub
		TaskService taskService = processEngine.getTaskService();
		Task task = taskService.getTask(dbid);

		EnvironmentFactory environmentFactory = (EnvironmentFactory) processEngine;

		environmentFactory.openEnvironment();
		OpenTask openTask = (OpenTask) task;
		openTask.delete("no reason");
	}

	@Override
	public boolean checkLogin(String username, String password)
			throws Exception {
		// TODO Auto-generated method stub
		IdentityService identityService = processEngine.getIdentityService();

		UserImpl user = (UserImpl) identityService.findUserById(username);

		return (user != null) && password.equals(user.getPassword());
	}

	@Override
	public void completeTask(String dbid, String transitionName,
			Map<String, Object> variables) throws Exception {
		// TODO Auto-generated method stub
		TaskService taskService = processEngine.getTaskService();
		taskService.setVariables(dbid, variables);
		taskService.completeTask(dbid, transitionName);
	}

	@Override
	public void deployXml(String name, InputStream inputStream)
			throws Exception {
		// TODO Auto-generated method stub
		RepositoryService repositoryService = processEngine
				.getRepositoryService();
		NewDeployment deploy = repositoryService.createDeployment();
		// deploy.setName(inputStream.toString());

		StringBuffer sb = new StringBuffer();
		byte[] buffer = new byte[1024];
		int len;
		while ((len = inputStream.read(buffer)) > 0) {
			String read = new String(buffer, 0, len);
			sb.append(read);
		}

		System.out.println(sb.toString());
		deploy.setName(sb.toString());
		deploy.setTimestamp(System.currentTimeMillis());
		// deploy=deploy.addResourceFromString(name, sb.toString());
		deploy.deploy();
		// deploy.setTimestamp(new )
		// deploy=deploy.addResourceFromInputStream(name,inputStream);
	}

	public void deployXml(String name, StringBuffer sb)
			throws Exception {
		// TODO Auto-generated method stub
		RepositoryService repositoryService = processEngine
				.getRepositoryService();
		NewDeployment deploy = repositoryService.createDeployment();
		// deploy.setName(inputStream.toString()); 
		
//		System.out.println(sb.toString());
		deploy.setName(sb.toString());
		deploy.setTimestamp(System.currentTimeMillis());
		// deploy=deploy.addResourceFromString(name, sb.toString());
		deploy.deploy();
		// deploy.setTimestamp(new )
		// deploy=deploy.addResourceFromInputStream(name,inputStream);
	}

	@Override
	public void deployXml(String xml) throws Exception {
		// TODO Auto-generated method stub
		RepositoryService repositoryService = processEngine
				.getRepositoryService();
		NewDeployment deploy = repositoryService.createDeployment();
		deploy = deploy.addResourceFromString("process.jpdl.xml", xml);
		deploy.deploy();
	}

	@Override
	public void deployZip(InputStream inputStream) throws Exception {
		// TODO Auto-generated method stub
		RepositoryService repositoryService = processEngine
				.getRepositoryService();
		NewDeployment deploy = repositoryService.createDeployment();
		deploy = deploy.addResourcesFromZipInputStream(new ZipInputStream(
				inputStream));
		deploy.deploy();
	}

	@Override
	public void endProcessInstance(String id) throws Exception {
		// TODO Auto-generated method stub
		ExecutionService executionService = processEngine.getExecutionService();

		ProcessInstance pi = executionService.findProcessInstanceById(id);

		/**
		 * FIXME: 这个部分肯定是jBPM的bug，
		 * api里的Execution接口和ExecutionService接口竟然都没有suspend和resume方法。
		 */
		((ExecutionImpl) pi).suspend();
	}

	@Override
	public ActivityCoordinates getActivityCoordinates(String id)
			throws Exception {
		// TODO Auto-generated method stub
		RepositoryService repositoryService = processEngine
				.getRepositoryService();
		ExecutionService executionService = processEngine.getExecutionService();

		ProcessInstance pi = executionService.findProcessInstanceById(id);

		return repositoryService.getActivityCoordinates(pi
				.getProcessDefinitionId(), ((ActivityExecution) pi)
				.getActivityName());
	}

	@Override
	public List<Task> getGroupTasks(String username) throws Exception {
		// TODO Auto-generated method stub
		TaskService taskService = processEngine.getTaskService();

		return taskService.findGroupTasks(username);
	}

	@Override
	public List<Group> getGroups() throws Exception {
		// TODO Auto-generated method stub
		EnvironmentFactory environmentFactory = (EnvironmentFactory) processEngine;
		Environment env = environmentFactory.openEnvironment();

		IdentitySession identitySession = env.get(IdentitySession.class);

		return ((IdentitySessionImpl) identitySession).findGroups();
	}

	@Override
	public List<Group> getGroupsByUser(String id) throws Exception {
		// TODO Auto-generated method stub
		IdentityService identityService = processEngine.getIdentityService();

		return identityService.findGroupsByUser(id);
	}

	@Override
	public List<ActivityCoordinates> getHistoryActivities(
			String processInstanceId) throws Exception {
		// TODO Auto-generated method stub
		HistoryService historyService = processEngine.getHistoryService();
		HistoryActivityInstanceQuery query = historyService
				.createHistoryActivityInstanceQuery();
		List<HistoryActivityInstance> activities = query.executionId(
				processInstanceId).list();

		ProcessDefinitionImpl processDefinition = (ProcessDefinitionImpl) this
				.getProcessDefinitionByProcessInstanceId(processInstanceId);
		List<ActivityCoordinates> list = new ArrayList<ActivityCoordinates>();
		ActivityImpl start = processDefinition.getInitial();
		ActivityCoordinates startAc = start.getCoordinates();
		startAc = new ActivityCoordinatesImpl(startAc.getX(), startAc.getY(),
				48, 48);
		list.add(startAc);

		for (HistoryActivityInstance activity : activities) {
			String activityName = activity.getActivityName();
			ActivityImpl activityImpl = processDefinition
					.findActivity(activityName);
			ActivityCoordinates ac = activityImpl.getCoordinates();

			if (activityImpl.getType().equals("decision")
					|| activityImpl.getType().equals("fork")
					|| activityImpl.getType().equals("join")
					|| activityImpl.getType().equals("end")
					|| activityImpl.getType().equals("end-cancel")
					|| activityImpl.getType().equals("end-error")) {
				ac = new ActivityCoordinatesImpl(ac.getX(), ac.getY(), 48, 48);
			}

			list.add(ac);
		}

		return list;
	}

	@Override
	public List<HistoryActivityInstance> getHistoryActivitiesByProcessInstanceId(
			String processInstanceId) throws Exception {
		// TODO Auto-generated method stub
		HistoryService historyService = processEngine.getHistoryService();
		HistoryActivityInstanceQuery query = historyService
				.createHistoryActivityInstanceQuery();

		return query.executionId(processInstanceId).list();
	}

	@Override
	public List<Job> getJobs() throws Exception {
		// TODO Auto-generated method stub
		ManagementService managementService = processEngine
				.getManagementService();

		return managementService.createJobQuery().list();
	}

	@Override
	public List<Task> getPersonalTasks(String username) throws Exception {
		// TODO Auto-generated method stub
		TaskService taskService = processEngine.getTaskService();

		return taskService.findPersonalTasks(username);
	}

	@Override
	public ProcessDefinition getProcessDefinitionByProcessInstanceId(
			String processInstanceId) throws Exception {
		// TODO Auto-generated method stub
		ExecutionService executionService = processEngine.getExecutionService();
		ProcessInstance pi = executionService
				.findProcessInstanceById(processInstanceId);
		EnvironmentFactory environmentFactory = (EnvironmentFactory) processEngine;
		environmentFactory.openEnvironment();

		return ((ExecutionImpl) pi).getProcessDefinition();
	}

	@Override
	public List<ProcessInstance> getProcessInstances(String pdId)
			throws Exception {
		// TODO Auto-generated method stub
		ExecutionService executionService = processEngine.getExecutionService();

		return executionService.createProcessInstanceQuery()
				.processDefinitionId(pdId).list();
	}

	@Override
	public List<ProcessDefinition> getSuspendedProcessDefinitions()
			throws Exception {
		// TODO Auto-generated method stub
		RepositoryService repositoryService = processEngine
				.getRepositoryService();

		return repositoryService.createProcessDefinitionQuery().suspended()
				.orderAsc(ProcessDefinitionQuery.PROPERTY_NAME).list();
	}

	@Override
	public List<ProcessInstance> getSuspendedProcessInstances()
			throws Exception {
		// TODO Auto-generated method stub
		ExecutionService executionService = processEngine.getExecutionService();

		return executionService.createProcessInstanceQuery().suspended().list();
	}

	@Override
	public List<Task> getTasks() throws Exception {
		// TODO Auto-generated method stub
		TaskService taskService = processEngine.getTaskService();

		return taskService.createTaskQuery().list();
	}

	@Override
	public List<Transition> getTransitionsForSignalProcess(String id)
			throws Exception {
		// TODO Auto-generated method stub
		ExecutionService executionService = processEngine.getExecutionService();
		ProcessInstance pi = executionService.findProcessInstanceById(id);
		EnvironmentFactory environmentFactory = (EnvironmentFactory) processEngine;
		environmentFactory.openEnvironment();
		ExecutionImpl executionImpl = (ExecutionImpl) pi;
		Activity activity = executionImpl.getActivity();

		return activity.getOutgoingTransitions();
	}

	@Override
	public Set<String> getTransitionsForTask(String id) throws Exception {
		// TODO Auto-generated method stub
		TaskService taskService = processEngine.getTaskService();

		return taskService.getOutcomes(id);
	}

	@Override
	public List<User> getUsers() throws Exception {
		// TODO Auto-generated method stub
		IdentityService identityService = processEngine.getIdentityService();

		return identityService.findUsers();
	}

	@Override
	public Map<String, Object> getVariablesForProcess(String id)
			throws Exception {
		// TODO Auto-generated method stub
		ExecutionService executionService = processEngine.getExecutionService();
		Set<String> names = executionService.getVariableNames(id);

		return executionService.getVariables(id, names);
	}

	@Override
	public Map<String, Object> getVariablesForTask(String id) throws Exception {
		// TODO Auto-generated method stub
		TaskService taskService = processEngine.getTaskService();
		Set<String> names = taskService.getVariableNames(id);

		return taskService.getVariables(id, names);
	}

	@Override
	public void releaseTask(String dbid) throws Exception {
		// TODO Auto-generated method stub
		TaskService taskService = processEngine.getTaskService();
		taskService.assignTask(dbid, null);
	}

	@Override
	public void removeGroup(String id) throws Exception {
		// TODO Auto-generated method stub
		IdentityService identityService = processEngine.getIdentityService();
		identityService.deleteGroup(id);
	}

	@Override
	public void removeProcessDefinitionById(String id) throws Exception {
		// TODO Auto-generated method stub
		RepositoryService repositoryService = processEngine
				.getRepositoryService();
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
				.processDefinitionId(id).uniqueResult();
		repositoryService.deleteDeploymentCascade(pd.getDeploymentId());
	}

	@Override
	public void removeProcessInstance(String id) throws Exception {
		// TODO Auto-generated method stub
		ExecutionService executionService = processEngine.getExecutionService();
		executionService.deleteProcessInstance(id);
	}

	@Override
	public void removeUser(String id) throws Exception {
		// TODO Auto-generated method stub
		IdentityService identityService = processEngine.getIdentityService();
		identityService.deleteUser(id);
	}

	@Override
	public void resumeProcessDefinitionById(String id) throws Exception {
		// TODO Auto-generated method stub
		RepositoryService repositoryService = processEngine
				.getRepositoryService();
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
				.processDefinitionId(id).uniqueResult();
		repositoryService.resumeDeployment(pd.getDeploymentId());
	}

	@Override
	public void resumeProcessInstance(String id) throws Exception {
		// TODO Auto-generated method stub
		ExecutionService executionService = processEngine.getExecutionService();

		ProcessInstance pi = executionService.createProcessInstanceQuery()
				.suspended().processInstanceId(id).uniqueResult();

		/**
		 * FIXME: 这个部分肯定是jBPM的bug，
		 * api里的Execution接口和ExecutionService接口竟然都没有suspend和resume方法。
		 */
		((ExecutionImpl) pi).resume();
	}

	@Override
	public void signalProcess(String id, String transitionName,
			Map<String, Object> variables) throws Exception {
		// TODO Auto-generated method stub
		RepositoryService repositoryService = processEngine
				.getRepositoryService();
		ExecutionService executionService = processEngine.getExecutionService();
		repositoryService.createProcessDefinitionQuery()
				.processDefinitionId(id).uniqueResult();
		executionService.setVariables(id, variables);

		executionService.signalExecutionById(id, transitionName);
	}

	@Override
	public void startProcess(String id, Map<String, Object> variables)
			throws Exception {
		// TODO Auto-generated method stub
		RepositoryService repositoryService = processEngine
				.getRepositoryService();
		ExecutionService executionService = processEngine.getExecutionService();
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
				.processDefinitionId(id).uniqueResult();

		executionService.startProcessInstanceById(pd.getId(), variables);
	}

	@Override
	public void suspendProcessDefinitionById(String id) throws Exception {
		// TODO Auto-generated method stub
		RepositoryService repositoryService = processEngine
				.getRepositoryService();
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
				.processDefinitionId(id).uniqueResult();
		repositoryService.suspendDeployment(pd.getDeploymentId());
	}

	@Override
	public void suspendProcessInstance(String id) throws Exception {
		// TODO Auto-generated method stub
		ExecutionService executionService = processEngine.getExecutionService();

		ProcessInstance pi = executionService.findProcessInstanceById(id);

		/**
		 * FIXME: 这个部分肯定是jBPM的bug，
		 * api里的Execution接口和ExecutionService接口竟然都没有suspend和resume方法。
		 */
		((ExecutionImpl) pi).suspend();
	}

	@Override
	public void takeTask(String dbid, String username) throws Exception {
		// TODO Auto-generated method stub
		TaskService taskService = processEngine.getTaskService();
		taskService.takeTask(dbid, username);
	}

}
